One of the most important uses of XML is to provide a
way of describing and encapsulating relational data. Doing so requires a
mapping between two basic kinds of data structures: sets and trees. The
techniques shown in this section thus have a single goal: converting
the columns and rows that make up the sets derived from any SELECT statement into XML trees.
Note
that before XML came along, selected result sets would most likely be
exported to delimited text files for consumption by disparate systems.
Today, most data interchange favors the use of XML. In response,
developers have come to rely on XSL for Transformations (XSLT) as a
companion skill for translating XML into HTML, PDF, RTF, or any other
type of document.
Let’s look at how the SELECT...FOR XML syntax can automatically mark up relational data in a variety of ways. The simplest approach uses FOR XML RAW.
RAW Mode
When specified at the end of a SELECT statement, the keywords FOR XML RAW tell SQL Server to generate a one-XML-element-per-row structure. The FOR XML
statement has a few options that change its output from the default of
document fragments to well-formed documents with a slightly (compared to
a few other FOR XML options) reshaped structure. This is its syntax:
FOR XML RAW [ ('ElementName') ]
[
[ , BINARY BASE64 ]
[ , TYPE ]
[ , ROOT [ ('RootName') ]
]
[ , { XMLDATA | XMLSCHEMA [ ('TargetNameSpaceURI') ]} ]
[ , ELEMENTS [ XSINIL | ABSENT ] ]
Listing 1 illustrates the XML generated by the no-option version of FOR XML RAW.
Listing 1. A SELECT Statement That Uses FOR XML RAW with No Additional Modifiers
SELECT Name, ListPrice, Color FROM Production.Product [Product] WHERE Name LIKE '%Chain%' ORDER BY Name FOR XML RAW go <row Name="Chain" ListPrice="20.2400" Color="Silver" /> <row Name="Chain Stays" ListPrice="0.0000" /> <row Name="Chainring" ListPrice="0.0000" Color="Black" /> <row Name="Chainring Bolts" ListPrice="0.0000" Color="Silver" /> <row Name="Chainring Nut" ListPrice="0.0000" Color="Silver" />
|
This kind of XML shape is known as attribute-centric
XML because each column in the result set is mapped to an attribute
rather than an element. Each row is mapped to an element named row, which holds these attributes.
Listing 2 illustrates how the resultant XML can be changed into an element-centric shape, where each selected column is converted to an XML element simply through the addition of the ELEMENTS keyword to FOR XML RAW.
Listing 2. A SELECT Statement That Uses FOR XML RAW, ELEMENTS
SELECT Name, ListPrice, Color FROM Production.Product [Product] WHERE Name LIKE '%Chain%' ORDER BY Name FOR XML RAW, ELEMENTS go <row> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> </row> <row> <Name>Chain Stays</Name> <ListPrice>0.0000</ListPrice> </row> <row> <Name>Chainring</Name> <ListPrice>0.0000</ListPrice> <Color>Black</Color> </row> <row> <Name>Chainring Bolts</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> </row> <row> <Name>Chainring Nut</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> </row>
|
If the tag name row is undesirable, you can change the element name by simply adding a string-valued parameter, in parentheses, to the RAW keyword.
Note that in contrast to FOR XML AUTO (discussed later in this chapter), in this case, aliasing the Production.Product table has no effect on the output. Here’s an example:
SELECT Name, ListPrice, Color
FROM Production.Product [Product]
WHERE Name LIKE '%Chain%'
ORDER BY Name
FOR XML RAW('ChainElement'), ELEMENTS
SQL Server 2008 also enables you to return NULL column values in generated XML. Previously, when a NULL column value was returned in the result set when using FOR XML,
the null value was simply omitted from the XML: no attribute or element
was generated at all. In SQL Server 2008, by specifying the keyword XSINIL after ELEMENTS, you can ensure that all null values are represented in the XML.
Note how the xsi:nil="true"
attribute is produced for elements representing null column values. In
addition, SQL Server automatically adds the XML schema namespace
declaration to each node of the resulting fragment. This is required
under the rules of XML because this fragment uses a Boolean attribute
called nil, which is declared in the XML schema located at the specified URL. This, as well as the effect of the ELEMENTS keyword, is illustrated in Listing 3.
Listing 3. A SELECT Statement That Uses FOR XML RAW, ELEMENTS XSINIL
SELECT TOP 1 Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE '%Chain%' ORDER BY Name FOR XML RAW('ChainElement'), ELEMENTS XSINIL go <ChainElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> <Weight xsi:nil="true"/> </ChainElement>
|
Note that the XML results in Listing 3
happen to produce a well-formed XML document only because a single row
was selected: this one row acts as both the root of the document and its
entire content. All other XML results (including all the previous
listings) encapsulating two or more rows are actually just fragments.
To easily change these XML fragments to well-formed documents, you can apply the ROOT keyword to add a root node to the output, as shown in Listing 47.4.
Listing 4. A SELECT Statement That Uses FOR XML RAW and the ROOT Keyword
SELECT Name, ListPrice, Color, Weight FROM Production.Product [Product] WHERE Name LIKE '%Chain%' ORDER BY Name FOR XML RAW('ChainElement'), ELEMENTS XSINIL, ROOT('ChainDoc') go <ChainDoc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ChainElement> <Name>Chain</Name> <ListPrice>20.2400</ListPrice> <Color>Silver</Color> <Weight xsi:nil="true" /> </ChainElement> <ChainElement> <Name>Chain Stays</Name> <ListPrice>0.0000</ListPrice> <Color xsi:nil="true" /> <Weight xsi:nil="true" /> </ChainElement> <ChainElement> <Name>Chainring</Name> <ListPrice>0.0000</ListPrice> <Color>Black</Color> <Weight xsi:nil="true" /> </ChainElement> <ChainElement> <Name>Chainring Bolts</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> <Weight xsi:nil="true" /> </ChainElement> <ChainElement> <Name>Chainring Nut</Name> <ListPrice>0.0000</ListPrice> <Color>Silver</Color> <Weight xsi:nil="true" /> </ChainElement> </ChainDoc>
|
Users (or applications) on the receiving side of RAW-produced
XML may also require an inline XML schema (XSD) or an inline XML-Data
Reduced (XDR) schema. Note that inline XDR schemas are considered to be
deprecated in this release.
To produce these schemas, you add the XMLSCHEMA or XMLDATA
keyword to the clause. The results are too long to be listed here, but
to see how these schema types differ, compare the output of this:
SELECT Name, ListPrice, Color, Weight
FROM Production.Product [Product]
WHERE Name LIKE '%Chain%'
ORDER BY Name
FOR XML RAW, ELEMENTS XSINIL, XMLDATA
to the output of this:
SELECT Name, ListPrice, Color, Weight
FROM Production.Product [Product]
WHERE Name LIKE '%Chain%'
ORDER BY Name
FOR XML RAW('ChainElement'),
ELEMENTS XSINIL,
ROOT('ChainDoc'),
XMLSCHEMA ('urn:www-samspublishing-com:examples')
Note
The XMLDATA keyword is not permitted when ROOT is specified or when a tag name parameter has been passed to RAW (for example, RAW('ChainElement')).
Note also that XMLSCHEMA
takes an optional string-valued parameter, allowing you to specify a
value for the target namespace of the produced XML (for example, XMLSCHEMA ('urn:www-samspublishing-com:examples)).